package com.zhoufy.tomcat;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.zhoufy.tomcat.config.Config;
import com.zhoufy.tomcat.server.HttpServlet;
import com.zhoufy.tomcat.utils.ClassLoadUtil;
import com.zhoufy.tomcat.utils.HttpProtocolUtil;
import com.zhoufy.tomcat.utils.Request;
import com.zhoufy.tomcat.utils.Response;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA by ChouFy on 2020-03-27.
 * <p>
 * miniCat 主类
 *
 * @author zhoufy
 */
public class MiniTomcat {


    private Map<String, HttpServlet> servletMap = new HashMap<>();

    /**
     * MiniCat 启动需要初始化展开的一些操作
     */
    public void start() throws Exception {
        Config config = Config.getInstance();
        ServerSocket server = new ServerSocket(config.getPort());

        System.out.println("tomcat start on port " + config.getPort());
//        miniCatFirst(server);
//        miniCatSecond(server);
//        miniCat(server);


        loadServerXML();


        miniCat(server);


        return;
    }


    private void loadServerXML() {

        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");

        SAXReader saxReader = new SAXReader();
        try {
            Document read = saxReader.read(resourceAsStream);
            Element rootElement = read.getRootElement();

            ClassLoadUtil classLoadUtil = new ClassLoadUtil("aa");

            List<Element> list = rootElement.selectNodes("//server");
            for (int i = 0; i < list.size(); i++) {
                Element element = list.get(i);
                List<Element> service = element.selectNodes("//service");
                for (Element serviceElement : service) {
                    Element connetor = (Element) serviceElement.selectSingleNode("connetor");
                    int port = Integer.parseInt(connetor.attributeValue("port"));

                    System.out.println(port);
                    Element engine = (Element) serviceElement.selectSingleNode("engine");

                    Element host = (Element) engine.selectSingleNode("host");

                    String appBase = host.attributeValue("appBase");
                    classLoadUtil.setPath(appBase);

                    System.out.println(appBase);
                    File[] files = new File(appBase).listFiles();

                    for (File file : files) {
                        if(file.isDirectory()){
                            loadServlet(file.getName(), appBase + File.separator + file.getName() + "/web.xml");
                        }
                    }

//                    loadServlet(appBase + "/web.xml");


                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                resourceAsStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


    private void miniCat(ServerSocket server) {
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                .setNameFormat("demo-pool-%d").build();

        ExecutorService threadPoolExecutor = new ThreadPoolExecutor(16, 32, 2L,
                TimeUnit.MINUTES, new LinkedBlockingQueue<>(50), namedThreadFactory, new ThreadPoolExecutor.CallerRunsPolicy());

//        loadServlet();
        while (true) {
            threadPoolExecutor.execute(() -> {
                try {
                    Socket socket = server.accept();
                    InputStream inputStream = socket.getInputStream();
                    Request request = new Request(inputStream);
                    OutputStream outputStream = socket.getOutputStream();
                    Response response = new Response(outputStream);
                    if (servletMap.get(request.getUrl()) == null) {
                        response.outputHTML(request.getUrl());
                    } else {
                        HttpServlet httpServlet = servletMap.get(request.getUrl());
                        httpServlet.service(request, response);
                    }
                    inputStream.close();
                    outputStream.close();
                    socket.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }

    /**
     * 加载解析 web.xml  初始化servlet
     */
    private void loadServlet(String  proName, String path) {

        InputStream resourceAsStream = null;
        try {
            File file = new File(path);
            boolean exists = file.exists();
            if(!exists){
                System.out.println("文件不存在");
                return ;
            }
            resourceAsStream = new FileInputStream(file);
            SAXReader saxReader = new SAXReader();
            Document read = saxReader.read(resourceAsStream);
            Element rootElement = read.getRootElement();

            List<Element> list = rootElement.selectNodes("//servlet");
            for (int i = 0; i < list.size(); i++) {
                Element element = list.get(i);
                Element servletName = (Element) element.selectSingleNode("servlet-name");
                String servletNameValue = servletName.getStringValue();

                Element servletClass = (Element) element.selectSingleNode("servlet-class");
                String servletClassValue = servletClass.getStringValue();

                // 根据servlet-name的值找到url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletNameValue + "']");
                String urlPatternValue = servletMapping.selectSingleNode("url-pattern").getStringValue();
                try {
                    HttpServlet o = (HttpServlet) Class.forName(servletClassValue).newInstance();
                    servletMap.put("/" + proName + urlPatternValue, o);
                } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (resourceAsStream != null) {
                    resourceAsStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void miniCatSecond(ServerSocket server) throws IOException {
        while (true) {
            Socket socket = server.accept();
            InputStream inputStream = socket.getInputStream();
            Request request = new Request(inputStream);
            OutputStream outputStream = socket.getOutputStream();
            Response response = new Response(outputStream);

            response.outputHTML(request.getUrl());
            socket.close();
        }
    }

    private void miniCatFirst(ServerSocket server) throws IOException {
        while (true) {
            Socket socket = server.accept();
            //  有了socket 接到到请求获取输出流
            OutputStream outputStream = socket.getOutputStream();
            String data = UUID.randomUUID().toString();
            String responseText = HttpProtocolUtil.getHttpHeader200(data.length()) + data;
            outputStream.write(responseText.getBytes());
            outputStream.close();
            socket.close();
        }
    }

    /**
     * miniCat  启动入口
     *
     * @param args
     */
    public static void main(String[] args) {

        MiniTomcat miniTomcat = new MiniTomcat();
        try {
            miniTomcat.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
